using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/// \class  gkPhysicController
/// \brief  Sum all the physic controller to obtain a unique velocity command
[AddComponentMenu("GK/Engine/Core/Physic/gkPhysicController")]
public class gkPhysicController : MonoBehaviour 
{
	/// \brief  The Physic handler prototype
	public delegate void PhysicUpdateHandler();
	
	/// The Physic components
	private List<gkPhysicComponent> m_rPhysicComponents = new List<gkPhysicComponent>();
	
	/// The addition of all the physics components linear velocities
	private Vector3 m_f3LinearVelocity;
	
	/// The addition of all the physics components linear velocities
	private Vector3 m_f3AngularVelocity;
	
	/// The physic update handler
	private List<PhysicUpdateHandler> m_rPhysicUpdateHandlers = new List<PhysicUpdateHandler>();
	
	/// The physic update handler
	private List<PhysicUpdateHandler> m_rPhysicUpdateHandlersToRemove = new List<PhysicUpdateHandler>();
	
	/// Are we notifying of the physic update
	private bool m_bNotifyingPhysicUpdate;
	
	/// \brief  Called each frame
    protected virtual void LateUpdate()
    {
		// Update the physic controller
		UpdatePhysicController();
		
		// Notify the physic update handlers
		NotifyPhysicUpdateHandlers();
    }
	
	/// \brief  Update the Pphysic controller
	protected void UpdatePhysicController()
	{
		ComputeLinearVelocity();
		ComputeAngularVelocity();
		
		ProcessAngularMovement();
		ProcessLinearMovement();		
	}
	
	/// \brief  Register a physic component
	public void RegisterPhysicComponent(gkPhysicComponent a_rPhysicComponent)
	{
		// Add the physic component
		m_rPhysicComponents.Add(a_rPhysicComponent);
		
		// Plug the handler
		a_rPhysicComponent.OnSetLinearVelocity = OnSetLinearVelocity;
		a_rPhysicComponent.OnSetAngularVelocity = OnSetAngularVelocity;
		
		// Ensure we are up to date regarding the velocities 
		ComputeAngularVelocity();
		ComputeLinearVelocity();
		OnSetAngularVelocity();
		OnSetLinearVelocity();
	}
	
	/// \brief  Add a physic update handler
	public void AddPhysicUpdateHandler(PhysicUpdateHandler a_rPhysicUpdateHandler)
	{
		// Add the handler to the list
		m_rPhysicUpdateHandlers.Add(a_rPhysicUpdateHandler);
	}
	
	/// \brief  Remove a physic update handler
	public void RemovePhysicUpdateHandler(PhysicUpdateHandler a_rPhysicUpdateHandler)
	{
		if(m_bNotifyingPhysicUpdate)
		{
			// Add the handler to the wait to be remove list
			m_rPhysicUpdateHandlersToRemove.Add(a_rPhysicUpdateHandler);
		}
		else
		{
			// Remove the handler of the list
			m_rPhysicUpdateHandlers.Remove(a_rPhysicUpdateHandler);
		}
	}
	
	/// \brief  get the addition of all the physics components angular velocities
	public Vector3 GetAngularVelocity()
	{
		return m_f3AngularVelocity;
	}
	
	/// \brief  get the addition of all the physics components linear velocities
	public Vector3 GetLinearVelocity()
	{
		return m_f3LinearVelocity;
	}
	
	/// \brief Called after angular velocity change
    void OnSetAngularVelocity()
    {
		ComputeAngularVelocity();
		OnAngularVelocityChange();
    }
	
	/// \brief  Called after total linear velocity change
    void OnSetLinearVelocity()
	{
		ComputeLinearVelocity();
		OnLinearVelocityChange();
    }
	
	/// \brief  Make the linear movement
    protected virtual void ProcessLinearMovement()
    {
    }

    // \brief  Make the angular movement
    protected virtual void ProcessAngularMovement()
    {
    }
	
	/// \brief Called after angular velocity change
    protected virtual void OnAngularVelocityChange()
    {
    }
	
	/// \brief  Called after total linear velocity change
    protected virtual void OnLinearVelocityChange()
	{
    }
	
	/// \brief  Update the Linear Velocity
	void ComputeLinearVelocity()
	{
		m_f3LinearVelocity = Vector3.zero;
		// Loop through each physic components to register at the velocities updates
		foreach(gkPhysicComponent rPhysicComponent in m_rPhysicComponents)
		{	
			if(rPhysicComponent.enabled)
			{
				m_f3LinearVelocity += rPhysicComponent.GetLinearVelocity();
			}
		}
	}
	
	/// \brief  Update the Linear Velocity
	void ComputeAngularVelocity()
	{
		m_f3AngularVelocity = Vector3.zero;
		// Loop through each physic components to register at the velocities updates
		foreach(gkPhysicComponent rPhysicComponent in m_rPhysicComponents)
		{	
			if(rPhysicComponent.enabled)
			{
				m_f3AngularVelocity += rPhysicComponent.GetAngularVelocity();
			}
		}
	}
	
	/// \brief  Notify the physic update handlers
	void NotifyPhysicUpdateHandlers()
	{
		// Lock the handler direct removal
		m_bNotifyingPhysicUpdate = true;
		
		// loop through the handler of the physic update and call them
		foreach(PhysicUpdateHandler rPhysicUpdateHandler in m_rPhysicUpdateHandlers)
		{
			// Call the current physic update handler
			rPhysicUpdateHandler();
		}
		
		// Unlock the handler direct removal
		m_bNotifyingPhysicUpdate = false;
		
		// Remove the handler that asked to be removed during the notify update
		RemovePhysicUpdateHandlers();
	}
	
	/// \brief  Remove the physic update handlers that asked to be remove
	void RemovePhysicUpdateHandlers()
	{
		// loop through the handler of the physic update that need to be removed and remove them
		foreach(PhysicUpdateHandler rPhysicUpdateHandler in m_rPhysicUpdateHandlersToRemove)
		{
			// Remove the current handler
			m_rPhysicUpdateHandlers.Remove(rPhysicUpdateHandler);
		}

		// Clear the array
		m_rPhysicUpdateHandlersToRemove.Clear();
	}
}
